package org.skydiveapplibrary.ui;

import java.lang.ref.WeakReference;
import java.util.List;

import org.skydiveapplibrary.R;
import org.skydiveapplibrary.data.LogEntryImage;
import org.skydiveapplibrary.ui.ImageManager;

import android.app.ActivityManager;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.support.v4.util.LruCache;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;

public class ImageAdapter extends BaseAdapter
{
	private Context mContext;
	private ImageManager imageManager;
	private List<LogEntryImage> logEntryImages;
	private Bitmap mPlaceHolderBitmap;
	private Integer imageSize = 0;
	private static LruCache<String, Bitmap> mMemoryCache;
	
	public ImageAdapter(Context context)
	{
		mContext = context;
		this.imageManager = new ImageManager(context);
		mPlaceHolderBitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.image_placeholder);
		
		// Get memory class of this device, exceeding this amount will throw an
		// OutOfMemory exception.
		final int memClass = ((ActivityManager) context.getSystemService(
				Context.ACTIVITY_SERVICE)).getMemoryClass();

		// Use 1/4 of the available memory for this memory cache.
		final int memoryCacheSize = 1024 * 1024 * memClass / 4;

		mMemoryCache = new LruCache<String, Bitmap>(memoryCacheSize)
		{
			@Override
			protected int sizeOf(String key, Bitmap bitmap)
			{
				// The cache size will be measured in bytes rather than number of items.
				return imageManager.getSizeInBytes(bitmap);
			}
		};		
	}
	
	public void refreshImages(List<LogEntryImage> logEntryImages)
	{
		this.logEntryImages = logEntryImages;
		notifyDataSetChanged();
	}
	
	@Override
	public int getCount()
	{
		if (logEntryImages == null)
			return 0;
		
		return logEntryImages.size();
	}

	@Override
	public LogEntryImage getItem(int position)
	{
	  return logEntryImages.get(position);
	}

	@Override
	public long getItemId(int position)
	{
		return 0;
	}
	
	@Override
	public View getView(int position, View convertView, ViewGroup container)
	{
		ImageView imageView;
		if (convertView == null) // if it's not recycled, initialize some attributes
		{
			imageView = new ImageView(mContext);
			imageView.setBackgroundColor(mContext.getResources().getColor(android.R.color.white));
			imageView.setLayoutParams(new GridView.LayoutParams(imageSize, imageSize));
			imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
			imageView.setPadding(0, 0, 0, 0);
		}
		else
		{
			imageView = (ImageView) convertView;
		}
				
		loadBitmap(logEntryImages.get(position), imageView);
		
		return imageView;
	}

	private void loadBitmap(LogEntryImage logEntryImage, ImageView imageView)
	{
		if (cancelPotentialWorker(logEntryImage.getFilename(), imageView))
		{
			Bitmap bitmap = getBitmapFromMemCache(logEntryImage.getFilename());
			
			if (bitmap != null)
			{
				imageView.setImageBitmap(bitmap);
			}
			else
			{
				final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
				final AsyncDrawable asyncDrawable =
						new AsyncDrawable(mContext.getResources(), mPlaceHolderBitmap, task);
			
				imageView.setImageDrawable(asyncDrawable);
				task.execute(logEntryImage);
			}
		}
	}
	
	public Integer getImageSize()
	{
		return imageSize;
	}
	
	public void setImageSize(Integer imageSize)
	{
		this.imageSize = imageSize;
	}
	
	
	public void addBitmapToCache(String key, Bitmap bitmap)
	{
		// Add to memory cache as before
		if (getBitmapFromMemCache(key) == null && this.imageSize > 0)
		{
			mMemoryCache.put(key, bitmap);
		}
	}

	private Bitmap getBitmapFromMemCache(String key)
	{
		return mMemoryCache.get(key);
	}

	static class AsyncDrawable extends BitmapDrawable
	{
		private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;

		public AsyncDrawable(Resources res, Bitmap bitmap,
				BitmapWorkerTask bitmapWorkerTask)
		{
			super(res, bitmap);
			bitmapWorkerTaskReference =
				new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
		}
		public BitmapWorkerTask getBitmapWorkerTask()
		{
			return bitmapWorkerTaskReference.get();
		}
	}
	
	public static boolean cancelPotentialWorker(String fileName, ImageView imageView)
	{
		final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);

		if (bitmapWorkerTask != null)
		{
			final LogEntryImage bitmapData = bitmapWorkerTask.logEntryImage;
			
			if (bitmapData == null || !fileName.equals(bitmapData.getFilename()))
			{
				// Cancel previous task
				bitmapWorkerTask.cancel(true);
			}
			else
			{
				// The same work is already in progress
				return false;
			}
		}
		// No task associated with the ImageView, or an existing task was cancelled
		return true;
	}

	private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView)
	{
		if (imageView != null)
		{
			final Drawable drawable = imageView.getDrawable();
			if (drawable instanceof AsyncDrawable)
			{
				final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
				return asyncDrawable.getBitmapWorkerTask();
			}
		}
		return null;
	}
	
	class BitmapWorkerTask extends AsyncTask<LogEntryImage, Void, Bitmap>
	{
		private final WeakReference<ImageView> imageViewReference;
		private LogEntryImage logEntryImage;

		public BitmapWorkerTask(ImageView imageView)
		{
			// Use a WeakReference to ensure the ImageView can be garbage collected
			imageViewReference = new WeakReference<ImageView>(imageView);
		}

		// Decode image in background.
		@Override
		protected Bitmap doInBackground(LogEntryImage... params)
		{
			logEntryImage = params[0];

			return imageManager.loadToSize(logEntryImage.getFilename(), imageSize, imageSize);
		}

		// Once complete, see if ImageView is still around and set bitmap.
		@Override
		protected void onPostExecute(Bitmap bitmap)
		{
			if (imageViewReference != null && bitmap != null)
			{
				final ImageView imageView = imageViewReference.get();
				if (imageView != null)
				{
					addBitmapToCache(logEntryImage.getFilename(), bitmap);
					imageView.setImageBitmap(bitmap);
				}
			}
		}
	}
}
